<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>音频可视化</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    body {
      width: 100%;
      height: 100%;
      background-color: #000000;
      overflow: hidden;
    }

    .oper-btns {
      margin: 0 auto;
      display: flex;
      justify-content: center;
      align-content: center;
      align-items: center;
      background-color: #ffffff;
      border-radius: 5px;
      overflow: hidden;
    }

    .oper-btns div {
      padding: 10px 12px;
      border-radius: 5px;
      user-select: none;
      overflow: hidden;
    }

    .oper-btns .play {
      width: 10px;
      height: 10px;
      background-image: url(./pause.png);
      background-repeat: no-repeat;
      background-size: 100% 100%;
      cursor: pointer;
    }

    .oper-btns .play.pause {
      width: 10px;
      height: 10px;
      background-image: url(./play.png) !important;
      background-repeat: no-repeat;
      background-size: 100% 100%;
      cursor: pointer;
    }

    .oper-btns .btn:hover {
      background-color: #ffffff;
      color: #000000;
      cursor: pointer;
      mix-blend-mode: difference;
    }
  </style>
</head>

<body>
  <div class="box">
    <canvas id="oscilloscope" height="500" style="width:100vw;height:100vh;"></canvas>
  </div>
  <div class="oper-btns">
    <div class="btn prev">上一曲</div>
    <div class="play pause"></div>
    <div class="btn next">下一曲</div>
    <div class="btn oscilloscope">切换示波器</div>
  </div>
  <div id="js-music-name" style="text-align: center;margin-top: 20px;color:#ffffff;"></div>
  <audio id="audio1" style="display: none;" controls crossOrigin='anonymous' src="柏林之声.mp3"></audio>
  <script>
    (function () {
      let audioDom = document.querySelector('#audio1');
      let volumeStep = 0.1;
      let isInit = false;
      let analyser = null;
      let drawVisual;
      let data = new Uint8Array(128);
      let bufferLength = 0;
      // 获取 ID 为 "oscilloscope" 的画布
      let canvas = document.getElementById("oscilloscope");
      let canvasCtx = canvas.getContext("2d");
      let colors = ["	#FF0000", "#AEEEEE", "#B23AEE", "#FFFF00", "#EEDFCC"];
      let musics = [
        "music1.mp3",
        "柏林之声.mp3",
        '风的季节.mp3',
        '新白娘子传奇1.mp3',
        '新白娘子传奇2.mp3',
        "反转地球（潘玮柏）.mp3",
        "冬天的秘密-en.mp3",
        "崔子格 - 卜卦.mp3"
      ]

      let prevBtn = document.querySelector('.prev')
      let playBtn = document.querySelector('.play')
      let nextBtn = document.querySelector('.next')
      let musicNameShowDom = document.querySelector('#js-music-name');
      let oscilloscopeFlag = true;
      let oscilloscopeBtn = document.querySelector('.oscilloscope');


      function getCurrentPlayMusicLR(tag) {
        const curMusicName = audioDom.getAttribute('src');
        const musicsMaxIndex = musics.length - 1;
        let curIndex = musics.findIndex((item) => {
          return curMusicName == item;
        });
        if (tag === 'prev') {
          if (curIndex === 0) {
            return musics[musicsMaxIndex];
          } else {
            return musics[curIndex - 1];
          }
        } else if (tag === 'next') {
          if (curIndex === musicsMaxIndex) {
            return musics[0];
          } else {
            return musics[curIndex + 1];
          }
        } else {
          return curMusicName;
        }
      }

      function init() {
        musicNameShowDom.innerHTML = getCurrentPlayMusicLR();
        prevBtn.addEventListener('click', () => {
          cancelAnimationFrame(drawVisual);
          audioDom.pause();
          const prevMusicName = getCurrentPlayMusicLR('prev');
          audioDom.setAttribute('src', prevMusicName);
          musicNameShowDom.innerHTML = prevMusicName;
          audioDom.play();
        }, false);
        nextBtn.addEventListener('click', () => {
          cancelAnimationFrame(drawVisual);
          audioDom.pause();
          const nextMusicName = getCurrentPlayMusicLR('next');
          audioDom.setAttribute('src', nextMusicName);
          musicNameShowDom.innerHTML = nextMusicName;
          audioDom.play();
        }, false);
        let isPlay = false
        document.addEventListener('keydown', (ev) => {
          if (ev.code === 'Space') {
            if (isPlay) {
              audioDom.pause();
              isPlay = false;
            } else {
              audioDom.play();
              isPlay = true;
            }
          } else if (ev.code === 'ArrowLeft') {
            prevBtn.click();
          } else if (ev.code === 'ArrowRight') {
            nextBtn.click();
          } else if (ev.code === 'ArrowUp') {
            ev.preventDefault();
            let curVolume = audioDom.volume;
            if (curVolume < 1) {
              audioDom.volume += volumeStep;
              if (audioDom.volume > 1) {
                audioDom.volume = 1;
              }
            }
          } else if (ev.code === 'ArrowDown') {
            ev.preventDefault();
            let curVolume = audioDom.volume;
            if (curVolume > 0.1) {
              audioDom.volume -= volumeStep;
              if (audioDom.volume < 0) {
                audioDom.volume = 0;
              }
            }
          } else if (ev.code === 'Tab') {
            ev.preventDefault();
            oscilloscopeBtn.click();
          }
        }, false)

        playBtn.addEventListener('click', () => {
          if (isPlay) {
            audioDom.pause();
            isPlay = false;
          } else {
            audioDom.play();
            isPlay = true;
          }
        }, false);

        oscilloscopeBtn.addEventListener('click', () => {
          oscilloscopeFlag = !oscilloscopeFlag;
          audioDom.pause();
          audioDom.play();
        }, false);
        let clientWidth = document.documentElement.clientWidth || document.body.clientWidth;
        let clientHeight = document.documentElement.clientHeight || document.body.clientHeight;

        canvas.setAttribute('width', clientWidth);
        canvas.setAttribute('height', clientHeight);
        canvasCtx.clearRect(0, 0, canvas.clientWidth, canvas.height)
        canvasCtx.fillStyle = "rgb(0,0,0)";
        canvasCtx.fillRect(0, 0, canvas.clientWidth, canvas.height);

        audioDom.addEventListener('pause', () => {
          cancelAnimationFrame(drawVisual);
          playBtn.className = 'play pause';
        }, false)

        audioDom.addEventListener('play', (ev) => {
          playBtn.className = 'play';
          if (isInit) {
            if (oscilloscopeFlag) {
              draw1();
            } else {
              draw2();
            }
            return;
          } else {
            let audioCtx = new AudioContext();
            let source = audioCtx.createMediaElementSource(audioDom);
            // analyser（挨呢来则） 分析器
            analyser = audioCtx.createAnalyser();
            analyser.fftSize = 256;
            source.connect(analyser);
            analyser.connect(audioCtx.destination);
            analyser.getByteFrequencyData(data);
            // bufferLength = analyser.frequencyBinCount;
            bufferLength = data.length;
            if (oscilloscopeFlag) {
              draw1();
            } else {
              draw2();
            }
            isInit = true;
          }
        }, false)
        audioDom.addEventListener('ended', () => {
          nextBtn.click();
        }, false)
      }


      // 绘制一个当前音频源的示波器
      function draw1() {
        drawVisual = requestAnimationFrame(draw1);
        analyser.getByteTimeDomainData(data);
        canvasCtx.fillStyle = "rgb(0,0,0)";
        canvasCtx.fillRect(0, 0, canvas.clientWidth, canvas.height);
        canvasCtx.lineWidth = 3;
        // 波浪条颜色
        canvasCtx.strokeStyle = colors[Math.round(Math.random() * colors.length)];
        canvasCtx.beginPath();
        var sliceWidth = (canvas.width * 1.0) / bufferLength;
        var x = 0;
        for (var i = 0; i < bufferLength; i++) {
          var v = data[i] / 256;
          var y = (v * canvas.height);
          if (i === 0) {
            canvasCtx.moveTo(x, y);
          } else {
            canvasCtx.lineTo(x, y);
          }
          x += sliceWidth;
        }
        canvasCtx.lineTo(canvas.width, canvas.height / 2);
        canvasCtx.stroke();
      }
      let start = undefined;
      // 绘制一个当前音频源的柱状图
      function draw2(timestamp) {
        drawVisual = requestAnimationFrame(draw2);
        if (!start) {
          start = timestamp;
        }

        let elapse = timestamp - start;
        if (elapse > 100) {
          start = undefined
          canvasCtx.clearRect(0, 0, canvas.clientWidth, canvas.height);
          analyser.getByteTimeDomainData(data);
          canvasCtx.fillStyle = "rgb(0,0,0)";
          canvasCtx.fillRect(0, 0, canvas.clientWidth, canvas.height);
          // 柱状颜色1
          canvasCtx.fillStyle = "#4169E1";
          // 柱状颜色2
          // canvasCtx.fillStyle = colors[Math.round(Math.random() * colors.length)];
          let barWidth = canvas.clientWidth / bufferLength;
          for (let i = 0; i < bufferLength; i++) {
            // 当前值除以最大值 获取到得值 不会大于1 乘以画布高度 拿到最终高度
            let barHeight = (data[i] / 256) * canvas.height;
            let x = barWidth * i;
            // 底部开始
            let y = canvas.height - barHeight;
            // let y = canvas.height - barHeight + (canvas.height - barHeight);
            canvasCtx.fillRect(x, y, barWidth - 3, barHeight);
          }
        }
      }

      init();
    })()
  </script>
</body>

</html>